use std::collections::VecDeque;

use async_trait::async_trait;
use bytes::Bytes;
use log::debug;

use crate::error::*;
use crate::http::base::parse::HttpResult;
use crate::modules::{HttpAccessHandle, HttpBodyFilterHandle};
use crate::utils::freelock::FreeLock;
use crate::{http::proxy::HttpProxy, modules::HttpHeaderFilterHandle};

pub struct HttpExample {
    next_header_filter: FreeLock<Option<&'static dyn HttpHeaderFilterHandle>>,
    next_body_filter: FreeLock<Option<&'static dyn HttpBodyFilterHandle>>,
}

impl HttpExample {
    pub fn new() -> Self {
        HttpExample {
            next_header_filter: FreeLock::new(None),
            next_body_filter: FreeLock::new(None),
        }
    }
}

#[async_trait]
impl HttpAccessHandle for HttpExample {
    fn get_name(&self) -> &str {
        "http_example"
    }

    async fn access_handle(&self, proxy: &mut HttpProxy) -> Result<()> {
        _ = proxy;
        debug!("http_example handle");
        Ok(())
    }
}

#[allow(dead_code)]
#[async_trait]
impl HttpHeaderFilterHandle for HttpExample {
    fn get_name(&self) -> &str {
        "http_example"
    }

    fn set_next(&self, next: &'static dyn HttpHeaderFilterHandle) {
        let mut guard = self.next_header_filter.lock();
        *guard = Some(next);
    }

    async fn header_filter(&self, proxy: &mut HttpProxy) -> Result<()> {
        debug!("example http header filter");
        self.next_header_filter
            .lock()
            .as_ref()
            .unwrap()
            .header_filter(proxy)
            .await
    }
}

#[allow(dead_code)]
#[async_trait]
impl HttpBodyFilterHandle for HttpExample {
    fn get_name(&self) -> &str {
        "http_example"
    }

    fn set_next(&self, next: &'static dyn HttpBodyFilterHandle) {
        let mut guard = self.next_body_filter.lock();
        *guard = Some(next);
    }

    async fn body_filter(
        &self,
        proxy: &mut HttpProxy,
        bufs: &mut VecDeque<Bytes>,
        done: bool,
    ) -> Result<HttpResult> {
        debug!("example http body filter");
        self.next_body_filter
            .lock()
            .as_ref()
            .unwrap()
            .body_filter(proxy, bufs, done)
            .await
    }
}
